use {
    crate::{
        base::client::Client,
        service::{coin, token},
    },
    alloy::primitives::U256,
    std::str::FromStr,
    tracing::{debug, error},
};

pub const APPROVE_GAS_READY: u128 = 10_000_000;

pub async fn token_collect(
    client: &Client,
    contract_str: &str,
    from_key_str: &str,
    payer_key_str: &str,
    to_str: &str,
    number_str: &str,
) -> anyhow::Result<String> {
    let principal_key_bytes = hex::decode(from_key_str)?;
    let principal = libsecp256k1::SecretKey::parse_slice(&principal_key_bytes)?;
    let principal_bs58 = wallet::tron_public_key_from_secret_key(&principal);
    let agent_key_bytes = hex::decode(payer_key_str)?;
    let agent = libsecp256k1::SecretKey::parse_slice(&agent_key_bytes)?;
    let agent_bs58 = wallet::tron_public_key_from_secret_key(&agent);
    let number = U256::from_str(number_str)?;
    let amount = number.to::<u128>();
    let approve_gas_ready = U256::from(APPROVE_GAS_READY);

    let allowance = token::allowance_token(
        client,
        contract_str,
        principal_bs58.as_str(),
        agent_bs58.as_str(),
    )
    .await?;

    if allowance < number {
        let account = coin::get_account(client, principal_bs58.as_str()).await?;
        let balance = U256::from(account.balance);
        if balance < approve_gas_ready {
            let supplemental = approve_gas_ready - balance + U256::from(1);
            let supplemental = supplemental.to::<u64>();
            let supplement_hash = coin::transfer_coin(
                client,
                payer_key_str,
                agent_bs58.as_str(),
                principal_bs58.as_str(),
                supplemental,
            )
            .await?;
            debug!("supplement coin: {supplemental}, hash: {supplement_hash}");
        }

        token::approve_token(
            client,
            contract_str,
            from_key_str,
            principal_bs58.as_str(),
            agent_bs58.as_str(),
            amount,
        )
        .await?;
    }

    let tx_hash = token::transfer_from_token(
        client,
        contract_str,
        payer_key_str,
        agent_bs58.as_str(),
        principal_bs58.as_str(),
        to_str,
        amount,
    )
    .await?;

    Ok(tx_hash)
}
